package com.tools.hry.xls.analysis.util;

import com.tools.hry.utils.BaseUtils;
import com.tools.hry.xls.analysis.AnalysisResult;
import com.tools.hry.xls.analysis.annotation.ExcelEntity;
import com.tools.hry.xls.analysis.annotation.ExcelProperty;
import com.tools.hry.xls.analysis.extension.ExcelRule;
import com.tools.hry.xls.exception.ExcelContentInvalidException;
import com.tools.hry.xls.exception.ExcelErrorLogBean;
import com.tools.hry.xls.exception.ExcelParseException;
import com.tools.hry.xls.exception.ExcelRegexpValidFailedException;
import org.apache.poi.ss.formula.functions.T;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by 峥桂 on 2017/9/5.
 */
public class Entity_FieldsUtils {


    /**
     * 从Excel中读取后存放检查日志信息
     */
    private List<ExcelErrorLogBean> errorLogBeans;
    /**
     * 规则对象缓存
     */
    @SuppressWarnings("rawtypes")
    private static Map<String, ExcelRule> rulesCache = new HashMap<String, ExcelRule>();


    public <T> List<T> toEntitys(Class<T> classType, String[] headers, String[][] datas) throws ExcelParseException {
        // 如果实体没有@ExcelEntity，则不允许继续操作
        ExcelEntity excelEntity = classType.getAnnotation(ExcelEntity.class);
        if (excelEntity == null) {
            throw new ExcelParseException("转换的实体必须存在@ExcelEntity!");
        }
        // 创建Excel实体字段信息
        List<ExcelEntityField> entityFieldList = this._getEntityFields(classType, headers,excelEntity);

        // 创建实体对象集
        List<T> entitys = new ArrayList<T>();
        errorLogBeans = new ArrayList<ExcelErrorLogBean>();// 存放校验日志信息
        int rowNum = 1;//第一行忽略
        int cellNum;
        ExcelErrorLogBean errorLogBean = null;
        int currErrRowNum = 0;
        try {
            // 遍历提交的数据行，依次填充到创建的实体对象中
            for (String[] data : datas) {
                rowNum++;
                if (data == null || data.length == 0) {
                    continue;
                }
                T obj = classType.newInstance();
                // 遍历实体对象的实体字段，通过反射为实体字段赋值
                cellNum = 0;
                for (ExcelEntityField eef : entityFieldList) {
                    if (currErrRowNum >= excelEntity.setThrowExceptionNum()) {
                        throw new ExcelParseException("当前已超过" + excelEntity.setThrowExceptionNum() + "行数据校验不通过，请核对后上传！");
                    }
                    cellNum++;
                    Method method = obj.getClass().getDeclaredMethod("set" +
                                    BaseUtils.toCapitalizeCamelCase(eef.getField().getName()),
                            eef.getField().getType());
                    try {
                        method.invoke(obj, _getFieldValue(data[eef.getIndex()], eef));
                    } catch (Exception e) {
                        //存放校验后的错误信息
                        errorLogBean = new ExcelErrorLogBean(rowNum, (eef.index+1), e.getMessage(), e, obj);
                        errorLogBeans.add(errorLogBean);
                        if (excelEntity.isRemoved()) {
                            //当前行中如果有任意一列校验不通过排除当前行
                            currErrRowNum++;
                        }
                    }
                }
                try {
                    T checkObj = (T) checkCurrRow(excelEntity, obj);
                    if (checkObj != null) {
                        entitys.add(checkObj);
                    } else {
                        entitys.add(obj);
                    }
                } catch (Exception e) {
                    errorLogBean = new ExcelErrorLogBean(rowNum, -1, e.getMessage(), e, obj);
                    errorLogBeans.add(errorLogBean);
                    currErrRowNum++;
                }
                if (currErrRowNum >= excelEntity.setThrowExceptionNum()) {
                    throw new ExcelParseException("当前已超过" + excelEntity.setThrowExceptionNum() + "行数据校验不通过，请核对后上传！");
                }

            }
        } catch (Exception e) {
            throw new ExcelParseException(e);
        }
        return entitys;
    }


    public List<ExcelErrorLogBean> getErrorLogBeans() {
        return errorLogBeans;
    }

    public void setErrorLogBeans(List<ExcelErrorLogBean> errorLogBeans) {
        this.errorLogBeans = errorLogBeans;
    }
    private <T> T checkCurrRow(ExcelEntity excelEntity, Object obj) throws ExcelParseException, ExcelContentInvalidException, IllegalAccessException, InstantiationException {
        Class<? extends ExcelRule> rule = excelEntity.rule();
        ExcelRule ruleObj;
        if (rulesCache.containsKey(rule.getName())) {
            ruleObj = rulesCache.get(rule.getName());
        } else {
            ruleObj = rule.newInstance();
            rulesCache.put(rule.getName(), ruleObj);
        }
        // 进行校验
        ruleObj.checkRow(obj);
        return (T) ruleObj.filterRow(obj);
    }

    /**
     * 获取Excel实体类中的填充字段
     *
     * @param classType
     * @param <T>
     * @return
     * @throws ExcelParseException
     */
    private <T> List<ExcelEntityField> _getEntityFields(Class<T> classType, String[] headers,ExcelEntity excelEntity) throws ExcelParseException {
        List<ExcelEntityField> eefs = new ArrayList<ExcelEntityField>();
        // 遍历所有字段
        Field[] allFields = classType.getDeclaredFields();
        int requiredCount=0;
        for (Field field : allFields) {
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            // 只对含有@ExcelProperty注解的字段进行赋值
            if (excelProperty == null) {
                continue;
            }

            String key = excelProperty.value().trim();// Excel Header名
            boolean required = excelProperty.required(); // 该列是否为必须列
            boolean isIgnore = excelProperty.isIgnore(); // 该列是否为忽略错误但需检查
            int index = _indexOfHeader(key, headers);
            // 如果字段必须，而索引为-1 ，说明没有这一列，抛错
            if (required && index == -1) {
                throw new ExcelParseException("表头列名不存在,正确列名[" + key + "]，请检查!");
            }
            if (required) {
                requiredCount++;
            }

            ExcelEntityField eef = new ExcelEntityField();
            eef.setColumnName(key);
            eef.setField(field);
            eef.setRequired(required);
            eef.setIgnore(isIgnore);
            eef.setIndex(index);
            eef.setAnnotation(excelProperty);
            eefs.add(eef);
        }

        if(excelEntity.isIgnoreRowCount() && headers.length>requiredCount){
            throw new ExcelParseException("表头列名数量与模板数量存在问题请检查!");
        }

        return eefs;
    }


    /**
     * 获取字段的值，路由不同的字段类型
     *
     * @param value
     * @param eef
     * @return
     * @throws ExcelParseException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ExcelContentInvalidException
     * @throws ExcelRegexpValidFailedException
     */
    @SuppressWarnings("rawtypes")
    private Object _getFieldValue(String value, ExcelEntityField eef) throws ExcelParseException, InstantiationException, IllegalAccessException, ExcelContentInvalidException, ExcelRegexpValidFailedException {
        // 进行规则校验
        ExcelProperty annotation = eef.getAnnotation();
        // 获取解析后的字段结果
        Object result = null;
        try {
            // 是否提交过来的是空值
            // 如果提交值是空值而且含有默认值的话
            // 则让提交过来的空值为默认值
            if (("".equals(value) || (value == null)) && annotation.hasDefaultValue()) {
                value = annotation.defaultValue();
            }
            result = _getFieldTypeValue(value, eef.getField(), annotation.regexp());
            if (annotation.required()) {
                if (result == null || "".equals(result.toString())) {
                    throw new ExcelParseException("列'" + eef.getColumnName() + "'是必须项!");
                }
            }
            if (annotation.isBaseDataChecked()) {//数据列最基本的检查
                String message = BaseDataChecked.checkBaseData(eef.getColumnName(), result);
                if (!"".equals(message) && message.length() > 0) {
                    throw new ExcelParseException("列 \"" + eef.getColumnName() + "\"校验异常:" + message + "!");
                }

            }

        } catch (ExcelRegexpValidFailedException e) {
            String errMsg = annotation.regexpErrorMessage();
            // 捕获正则验证失败异常
            if ("".equals(errMsg)) {
                errMsg = "列 " + eef.getColumnName() + " 没有通过规则验证!";
            }
            throw new ExcelContentInvalidException(errMsg, e);
        } catch (NumberFormatException e) {
            throw new ExcelContentInvalidException("列 " + eef.getColumnName() + " 数据类型错误!");
        } catch (NullPointerException e) {
            throw new ExcelContentInvalidException("列 " + eef.getColumnName() + " 不能为空!");
        }
        /**
         * 缓存已经实例化过的规则对象，避免每次都重新
         * 创建新的对象的额外消耗
         */
        Class<? extends ExcelRule> rule = annotation.rule();
        ExcelRule ruleObj;
        if (rulesCache.containsKey(rule.getName())) {
            ruleObj = rulesCache.get(rule.getName());
        } else {
            ruleObj = rule.newInstance();
            rulesCache.put(rule.getName(), ruleObj);
        }

        // 进行校验
        ruleObj.checkCell(result, eef.getColumnName(), eef.getField().getName());
        result = ruleObj.filterCell(result, eef.getColumnName(), eef.getField().getName());

        return result;
    }

    /**
     * 解析字段类型
     *
     * @param value
     * @param field
     * @return
     * @throws ExcelParseException
     * @throws ExcelContentInvalidException
     * @throws ExcelRegexpValidFailedException
     */
    @SuppressWarnings("rawtypes")
    private Object _getFieldTypeValue(String value, Field field, String regexp) throws
            ExcelParseException, ExcelContentInvalidException, ExcelRegexpValidFailedException {
        Class<?> type = field.getType();
        String typeName = type.getName();
        if (value == null || "".equals(value.trim()) || value.length() <= 0) {
            return null;
        }
        // 字符串
        if ("java.lang.String".equals(typeName)) {
            if (!"".equals(regexp) && !value.matches(regexp)) {
                throw new ExcelRegexpValidFailedException();
            }
            return value;
        }
        // 整形
        if ("java.lang.Double".equals(typeName) || "double".equals(typeName)) {
            if (!"".equals(regexp) && !value.matches(regexp)) {
                throw new ExcelRegexpValidFailedException();
            }
            return new BigDecimal(value).doubleValue();
        }
        // 长整形
        if ("java.lang.Long".equals(typeName) || "long".equals(typeName)) {
            if (!"".equals(regexp) && !value.matches(regexp)) {
                throw new ExcelRegexpValidFailedException();
            }
            return Long.parseLong(value);
        }
        // 整形
        if ("java.lang.Integer".equals(typeName) || "int".equals(typeName)) {
            if (!"".equals(regexp) && !value.matches(regexp)) {
                throw new ExcelRegexpValidFailedException();
            }
          try{
              return Integer.parseInt(value);
          }catch (Exception e){
              try{
                  return Integer.parseInt(value.substring(0,value.indexOf(".")));
              }catch (Exception ex){
                  throw new ExcelParseException("非整形错误！");
              }
          }
        }
//        // 整形
        if ("java.math.BigDecimal".equals(typeName)) {
            if (!"".equals(regexp) && !value.matches(regexp)) {
                throw new ExcelRegexpValidFailedException();
            }
            return new BigDecimal(value);
        }
        // 短整型
        if ("java.lang.Short".equals(typeName) || "short".equals(typeName)) {
            if (!"".equals(regexp) && !value.matches(regexp)) {
                throw new ExcelRegexpValidFailedException();
            }
            return Short.parseShort(value);
        }
        // Date型
        if ("java.util.Date".equals(typeName)) {
            value = value.replace("-", "/");
            if (value.length() <= 10) {
                value += " 00:00:00";
            }
            try {
                return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(value);
            } catch (ParseException e) {
                throw new ExcelParseException("日期类型格式有误!");
            }
        }

        // Timestamp
        if ("java.sql.Timestamp".equals(typeName)) {
            try {
                return new Timestamp(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(value).getTime());
            } catch (ParseException e) {
                throw new ExcelParseException("时间戳类型格式有误!");
            }
        }
        // Char型
        if ("java.lang.Character".equals(typeName) || "char".equals(typeName)) {
            if (value.length() == 1) {
                return value.charAt(0);
            }
        }
      /*  // 用户注册的自定义类型
        for (Class<? extends ExcelType> et : userDefinedType) {
            if (et.getName().equals(typeName)) {
                try {
                    ExcelType newInstance = et.newInstance();
                    return newInstance.parseValue(value);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }*/

        throw new ExcelParseException("不支持的字段类型 " + typeName + " !");
    }

    /**
     * 列名在列标题中的索引
     *
     * @param columnName
     * @return
     */
    private int _indexOfHeader(String columnName, String[] headers) {
        for (int i = 0; i < headers.length; i++) {
            if (headers[i].equals(columnName)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Excel实体字段类（内部类）
     */
    protected class ExcelEntityField {
        private String columnName;
        private boolean required;
        private boolean isIgnore;
        private Field field;
        private int index;
        private ExcelProperty annotation;

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public String getColumnName() {
            return columnName;
        }

        public void setColumnName(String columnName) {
            this.columnName = columnName;
        }

        public boolean isRequired() {
            return required;
        }

        public void setRequired(boolean required) {
            this.required = required;
        }

        public Field getField() {
            return field;
        }

        public void setField(Field field) {
            this.field = field;
        }

        public ExcelProperty getAnnotation() {
            return annotation;
        }

        public void setAnnotation(ExcelProperty annotation) {
            this.annotation = annotation;
        }

        public boolean isIgnore() {
            return isIgnore;
        }

        public void setIgnore(boolean ignore) {
            isIgnore = ignore;
        }
    }
}
